/*
 *      Copyright 2012-2013 Battams, Derek
 *       
 *       Licensed under the Apache License, Version 2.0 (the "License");
 *       you may not use this file except in compliance with the License.
 *       You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *       Unless required by applicable law or agreed to in writing, software
 *       distributed under the License is distributed on an "AS IS" BASIS,
 *       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *       See the License for the specific language governing permissions and
 *       limitations under the License.
 */
package org.schedulesdirect.grabber;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.fluent.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.schedulesdirect.api.Config;
import org.schedulesdirect.api.EpgClient;
import org.schedulesdirect.api.NetworkEpgClient;
import org.schedulesdirect.grabber.JsonRequest.RequestAction;
import org.schedulesdirect.grabber.JsonRequest.RequestObject;

/**
 * Download schedules in bulk
 * 
 * @author Derek Battams &lt;derek@battams.ca&gt;
 *
 */
class ScheduleTask implements Runnable {
	static private final Log LOG = LogFactory.getLog(ScheduleTask.class);
	static private final String SCHED_PREFIX = "sched_";
	
	private JSONArray req;
	private File vfs;
	private NetworkEpgClient clnt;
	private ProgramCache cache;

	/**
	 * Constructor
	 * @param req The array of station ids to be sent to the server for download
	 * @param vfs The name of the vfs used for storing EPG data
	 * @param clnt The EpgClient to be used to download the request
	 * @param cache The global cache of processed Program ids
	 */
	public ScheduleTask(JSONArray req, File vfs, NetworkEpgClient clnt, ProgramCache cache) throws JSONException {
		this.req = req;
		this.vfs = vfs;
		this.clnt = clnt;
		this.cache = cache;
	}

	@Override
	public void run() {
		long start = System.currentTimeMillis();
		try {
			JsonRequest req = new JsonRequest(clnt.getHash(), clnt.getUserAgent(), clnt.getBaseUrl());
			JSONObject resp = req.submitForJson(RequestAction.GET, RequestObject.SCHEDULES, this.req);
			if(!Utils.isErrorResponse(resp)) {
				String url;
				try {
					url = resp.getString("URL");
				} catch(JSONException e) {
					throw new IOException(e);
				}
				InputStream ins = Request.Get(url).execute().returnContent().asStream();
				if(Config.get().isCaptureZipResponsesActive()) {
					File fName = new File(String.format("sched-%s.zip", Thread.currentThread().getName()));
					OutputStream out = new FileOutputStream(fName);
					IOUtils.copy(ins, out);
					out.close();
					ins.close();
					ins = new FileInputStream(fName);
				}
				ZipInputStream zip = new ZipInputStream(ins);
				LOG.info(String.format("ScheduleTask download DONE! [%dms]", System.currentTimeMillis() - start));
				ZipEntry ze;
				while((ze = zip.getNextEntry()) != null) {
					String id = ze.getName();
					if(id.equals(EpgClient.SERVER_ID_ENTRY)) {
						LOG.debug(String.format("Request '%s' handled by %s", req.getTargetUrl(), IOUtils.toString(zip, "UTF-8")));
						continue;
					}
					id = id.substring(SCHED_PREFIX.length(), id.indexOf('.'));
					JSONArray sched = null;
					String input = IOUtils.toString(zip, "UTF-8");
					try {
						JSONObject schedObj = new JSONObject(input);
						sched = schedObj.getJSONArray("programs");
					} catch(JSONException e) {
						JSONObject err = new JSONObject(input);
						if(Utils.isErrorResponse(err))
							LOG.error(String.format("%s [id=%s]", err.optString("message"), id));
						else {
							LOG.debug(String.format("Unexpected JSONObject:%n%s", input));
							throw new JSONException("Unexpected JSONObject received!");
						}
					}
					if(sched != null) {
						Date expiry = new Date(System.currentTimeMillis() - Grabber.MAX_AIRING_AGE);
						for(int j = 0; j < sched.length(); ++j) {
							try {
								JSONObject airing = sched.getJSONObject(j);
								Date end = Utils.getEndDateForAiring(airing);
								String progId = airing.getString("programID");
								if(!end.before(expiry)) {
									String md5 = airing.getString("md5");
									cache.markIfDirty(progId, md5);
								} else
									LOG.debug(String.format("Expired airing discovered and ignored! [%s; %s; %s]", progId, id, end));
							} catch(JSONException e) {
								LOG.warn("JSONException", e);
							}
						}
						Utils.writeVfsEntry(vfs, String.format("schedules/%s.txt", id), sched.toString(2));
					}
				}
				zip.close();
				LOG.info(String.format("ScheduleTask COMPLETE! [%dms]", System.currentTimeMillis() - start));
			}
		} catch(JSONException e) {
			Grabber.failedTask = true;
			LOG.fatal("Fatal JSON error!", e);
			throw new RuntimeException(e);
		} catch(IOException e) {
			Grabber.failedTask = true;
			LOG.error("IOError receiving schedule data! Filling cache with empty schedules!", e);
			try {
				JSONArray ids = req;
				for(int i = 0; i < ids.length(); ++i) {
					String id = ids.getString(i);
					Utils.writeVfsEntry(vfs, String.format("schedules/%s.txt", id), "[]");
				}
			} catch(Exception x) {
				LOG.error("Unexpected error!", x);
				throw new RuntimeException(x);
			}
		}
	}
}
